#pragma once
#include "Sock.hpp"
 #include <poll.h>
const static uint16_t defaultport = 8080;
const static int N = 4096; 
const static short defaultevent = 0;
typedef struct pollfd type_t;

//poll接口的使用
class PollServer
{
public:
    PollServer(uint16_t port = defaultport) : port_(port)
    {
    }
    void InitServer()
    {
        // 1.创建socket套接字
        listensock_.Socket();
        // 2.Bind端口号，云服务器不需要绑定ip
        listensock_.Bind(port_);
        // 3.listen监听
        listensock_.Listen();
        fdarray = new type_t[N];   //创建节点

        //节点初始化
        for (int i = 0; i < N; i++)
        {
            fdarray[i].fd = defaultscok;
            fdarray[i].events = defaultevent;
            fdarray[i].revents = defaultevent;
        }
    }
    void Accepter()
    {
        std::cout << "有一个新的连接到来了" << std::endl;
        // 在这里进行Accept会不会阻塞？不会
        std::string clientip;
        uint16_t clientport;
        int sock = listensock_.Accept(&clientip, &clientport);
        if(sock < 0) return;
        // accept成功之后，能够直接进行read/recv吗？
        // 不能，因为你怎么知道sock上已经有数据，所以我们需要将sock交给select，让select帮我们管理。

        // 怎么保证select每次轮询的时候，监听内容已经更新了？
        // select服务器，使用的时候，需要程序员自己维护一个第三方数组，来进行已经获得的sock进行管理！
        logMessage(Debug, "[%s,%d],sock: %d", clientip.c_str(), clientport, sock);
        int pos;
        for (pos = 1; pos < N; pos++)
        {
            if (fdarray[pos].fd == defaultscok)
                break;
        }
        if (pos >= N)
        {
            // 服务器能管理的接口已经满了  -->这里可以进行扩容
            close(sock);
            logMessage(Warning, "服务器能管理的接口已经满了，新的客户端不可再建立连接了");
            return;
        }else
        {
           fdarray[pos].fd = sock;
           fdarray[pos].events = POLLIN;
        }
    }
    // 根据不同的套接字进行不同的操作
    void HandlerEvent()
    {
        for (int i = 0; i < N; i++)
        {
            if(fdarray[i].fd == defaultscok) continue;
            if(fdarray[i].fd == listensock_.Fd() && (fdarray[i].revents & POLLIN))
            {
                Accepter();
            }else if(fdarray[i].fd != listensock_.Fd() && (fdarray[i].revents & POLLIN))
            {
                char buffer[1024];
                ssize_t s = recv(fdarray[i].fd,buffer,sizeof(buffer) - 1,0);  //不会被阻塞
                if(s > 0)
                {
                    //ServerIO
                    buffer[s-1] = 0;
                    std::cout<<"client#"<<buffer<<std::endl;
                    fdarray[i].events |= POLLOUT;  //poll支持写的操作  
                    //将接收到消息回传过去--这里也需要进行select进行管理，因为发送缓冲区可能存在满的情况
                    std::string message = buffer;
                    message += "[select server echo]";
                    send(fdarray[i].fd,message.c_str(),message.size(),0);
                }else
                {
                    if(s == 0)
                    {
                        logMessage(Info,"client quit...,fdarray[i]->defaultscok:%d->%d",fdarray[i].fd,defaultscok);
                    }else
                    {
                        logMessage(Warning,",recv error,code:%d,string:%s,%d->%d",errno,strerror(errno),fdarray[i].fd,defaultscok);
                    }
                    close(fdarray[i].fd);
                    fdarray[i].fd = defaultscok;  //清除
                }
            }
        }
    }
    void Start()
    {
        fdarray[0].fd = listensock_.Fd();
        fdarray[0].events = POLLIN; //(POLLIN | POLLOUT)
        while (true)
        {
            int timeout = -1;
            int n = poll(fdarray,N,timeout);
            switch (n)
            {
            case 0:
                // 超时
                logMessage(Debug, "timeout %d %s", errno, strerror(errno));
                break;
            case -1:
                // 出错
                logMessage(Warning, "%d %s", errno, strerror(errno));
                break;
            default:
                // 成功
                logMessage(Debug, "有一个就绪事件发生了:%d", n);
                // 成功之后需要检查是否有可读事件发生
                HandlerEvent();
                DebugPrint();
                break;
            }
        }
    }
    // 将套接字打印出来看看
    void DebugPrint()
    {
        std::cout << "fdarray[]：";
        for (int i = 0; i < N; i++)
        {
            if (fdarray[i].fd != defaultscok)
            {
                std::cout << fdarray[i].fd << " ";
            }
        }
        std::cout << "\n";
    }
    ~PollServer()
    {
        listensock_.Close();
        if(fdarray) delete[] fdarray;
    }

private:
    uint16_t port_;
    Sock listensock_;
    type_t* fdarray;  
};